﻿using System;
using System.Reflection;
using MugenInjection.Delegates;
using MugenInjection.Exceptions;
using MugenInjection.Infrastructure;
using MugenInjection.Interception.Components;
using MugenInjection.Interception.Interface;
using MugenInjection.Interception.Interface.Components;
using MugenInjection.Interception.Syntax;
using MugenInjection.Interface;
using MugenInjection.Syntax;
using MugenInjection.Syntax.Constant;
using MugenInjection.Syntax.Method;
using MugenInjection.Syntax.Type;

namespace MugenInjection.Interception
{
    /// <summary>
    /// Represents the extensions for <see cref="IInjector"/> to add interceptor support.
    /// </summary>
    public static class InterceptionExtensionUtils
    {
        #region Const

        public const string AdditionalInterceptorKey = "_AdditionalInterceptorKey_";

        #endregion

        #region Fields

        /// <summary>
        /// Contain Proxy assembly name with public key.
        /// </summary>
        public const string ProxyAssemblyName =
            "MugenInjectionProxyAssembly, PublicKey=00240000048000009400000006020000002400005253413100040000010001005515f32c95d7ac72020d117fa0a25b0dc2be6483827ffe624875b838c9e02c43345e566d5159437f1eddf9ae9f16075f1a971394c1d0975b79a553bad5c28af1dbf1e36f5b5b0142918b33a90884e1621ef8bfc2a2433ab406e159cc3ad2ecb792686efaae6a60f7cf69016ca98ee2b0ebf8dcc85f9f0c0e21d22a1827b830aa";

        #endregion

        #region Internal methods

        internal static IInterceptionManagerComponent GetInterceptionManagerComponent(IInjector injector)
        {
            IInterceptionManagerComponent interceptionManagerComponent;
            if (!injector.Components.TryGet(out interceptionManagerComponent))
            {
                interceptionManagerComponent = new InterceptionManagerComponent();
                injector.Components.Add(interceptionManagerComponent);
            }
            return interceptionManagerComponent;
        }

        private static TSyntax InterceptAsTargetInternal<TSyntax>(IBindingBuilder bindingBuilder, Type[] interfaces,
                                                                   IInterceptorProcess[] interceptorProcesses,
                                                                   bool alwaysNewProxy)
        {
            return GetInterceptionManagerComponent(bindingBuilder.Injector)
                .InterceptAsTarget<TSyntax>(bindingBuilder, interfaces, alwaysNewProxy, interceptorProcesses);
        }

        private static TSyntax InterceptAsTargetInternal<TType, TSyntax>(IBindingBuilder bindingBuilder,
                                                                          Type[] interfaces,
                                                                          IInterceptorProcess[] interceptorProcesses,
                                                                          bool alwaysNewProxy)
        {
            return GetInterceptionManagerComponent(bindingBuilder.Injector)
                .InterceptAsTarget<TType, TSyntax>(bindingBuilder, interfaces, alwaysNewProxy, interceptorProcesses);
        }

        private static IInterceptTypeSyntax InterceptInternal(
            ICommonBindingSyntax commonBindingSyntax,
            Type[] interfaces, IInterceptorProcess[] interceptorProcess)
        {
            return GetInterceptionManagerComponent(commonBindingSyntax.Injector).Intercept(commonBindingSyntax,
                                                                                           interfaces,
                                                                                           interceptorProcess);
        }

        private static IInterceptTypeSyntax<T> InterceptInternal<T>(IGenericBindingSyntax<T> commonBindingSyntax, Type[] interfaces, IInterceptorProcess[] interceptorProcess)
        {
            return GetInterceptionManagerComponent(commonBindingSyntax.Injector)
                .Intercept(commonBindingSyntax, interfaces, interceptorProcess);
        }

        private static ActionInterceptorProcess GetOrAddActionInterceptorProcess(IInterceptSyntax syntax)
        {
            ActionInterceptorProcess actionInterceptor;
            if (!syntax.Settings.TryGet(AdditionalInterceptorKey, out actionInterceptor))
            {
                actionInterceptor = new ActionInterceptorProcess();
                syntax.Settings[AdditionalInterceptorKey] = actionInterceptor;
            }
            return actionInterceptor;
        }

        private static void AddActionInterceptor(ActionInterceptorProcess actionInterceptor, InterceptorType interceptorType, MemberInfo member, Action<IInterceptor> action)
        {
            MethodInfo method;
            switch (interceptorType)
            {
                case InterceptorType.Method:
                    method = ((MethodInfo)member);
                    break;
                case InterceptorType.PropertyGet:
                    method = ((PropertyInfo)member).GetGetMethod(true);
                    break;
                case InterceptorType.PropertySet:
                    method = ((PropertyInfo)member).GetSetMethod(true);
                    break;
                case InterceptorType.EventAdd:
                    method = ((EventInfo)member).GetAddMethod(true);
                    break;
                case InterceptorType.EventRemove:
                    method = ((EventInfo)member).GetRemoveMethod(true);
                    break;
                default:
                    throw Validate.EnumOutOfRange(interceptorType, "interceptorType");
            }
            if (method == null || !method.IsVirtual)
                throw new ArgumentException(string.Format("It is impossible intercept a non-virtual method {0}, make this method virtual or abstract before intercept it.", member));
            var interceptionMember = new ActionInterceptorProcess.InterceptionMember(interceptorType, method);
            if (actionInterceptor.Interceptors.ContainsKey(interceptionMember))
                throw InvalidBindingException.CreateDuplicateInvalidBindingException(string.Format("intercept member {0}", member));
            actionInterceptor.Interceptors[interceptionMember] = action;
        }

        #endregion

        #region Public methods

        /// <summary>
        /// Indicates that instances associated with this binding will be proxied.
        /// </summary>
        /// <param name="bindingSyntax">The binding syntax target.</param>
        /// <param name="interceptorProcesses">The specified <see cref="IInterceptorProcess"/> for intercept.</param>
        /// <returns>The fluent syntax.</returns>
        public static IInterceptTypeSyntax Intercept(ICommonBindingSyntax bindingSyntax,
                                                     params IInterceptorProcess[] interceptorProcesses)
        {
            Validate.ArgumentNotNull(bindingSyntax, "bindingSyntax");
            Validate.ArgumentNotNull(interceptorProcesses, "interceptorProcesses");
            return InterceptInternal(bindingSyntax, InjectorUtils.EmptyTypes, interceptorProcesses);
        }

        /// <summary>
        /// Indicates that instances associated with this binding will be proxied.
        /// </summary>
        /// <param name="bindingSyntax">The binding syntax target.</param>
        /// <param name="interfaces">Interfaces for proxy.</param>
        /// <param name="interceptorProcesses">The specified <see cref="IInterceptorProcess"/> for intercept.</param>
        /// <returns>The fluent syntax.</returns>
        public static IInterceptTypeSyntax Intercept(ICommonBindingSyntax bindingSyntax, Type[] interfaces,
                                                     params IInterceptorProcess[] interceptorProcesses)
        {
            Validate.ArgumentNotNull(bindingSyntax, "bindingSyntax");
            Validate.ArgumentNotNull(interceptorProcesses, "interceptorProcesses");
            return InterceptInternal(bindingSyntax, interfaces, interceptorProcesses);
        }

        //------

        /// <summary>
        /// Indicates that instances associated with this binding will be proxied.
        /// </summary>
        /// <param name="bindingSyntax">The binding syntax target.</param>
        /// <param name="interceptorProcesses">The specified <see cref="IInterceptorProcess"/> for intercept.</param>
        /// <returns>The fluent syntax.</returns>
        public static IInterceptTypeSyntax<T> Intercept<T>(IGenericBindingSyntax<T> bindingSyntax,
                                                     params IInterceptorProcess[] interceptorProcesses)
        {
            Validate.ArgumentNotNull(bindingSyntax, "bindingSyntax");
            Validate.ArgumentNotNull(interceptorProcesses, "interceptorProcesses");
            return InterceptInternal(bindingSyntax, InjectorUtils.EmptyTypes, interceptorProcesses);
        }

        /// <summary>
        /// Indicates that instances associated with this binding will be proxied.
        /// </summary>
        /// <param name="bindingSyntax">The binding syntax target.</param>
        /// <param name="interfaces">Interfaces for proxy.</param>
        /// <param name="interceptorProcesses">The specified <see cref="IInterceptorProcess"/> for intercept.</param>
        /// <returns>The fluent syntax.</returns>
        public static IInterceptTypeSyntax<T> Intercept<T>(IGenericBindingSyntax<T> bindingSyntax, Type[] interfaces,
                                                     params IInterceptorProcess[] interceptorProcesses)
        {
            Validate.ArgumentNotNull(bindingSyntax, "bindingSyntax");
            Validate.ArgumentNotNull(interceptorProcesses, "interceptorProcesses");
            return InterceptInternal(bindingSyntax, interfaces, interceptorProcesses);
        }

        /// <summary>
        /// Indicates that instances associated with this binding will be proxied and the target will be to the original binding.
        /// </summary>
        /// <param name="constantObjectSyntax">The binding syntax target.</param>
        /// <param name="interfaces">Interfaces for proxy.</param>
        /// <param name="interceptorProcesses">The specified <see cref="IInterceptorProcess"/> for intercept.</param>
        /// <returns>The fluent syntax.</returns>
        public static IInterceptAsTargetConstantSyntax InterceptAsTarget(IConstantObjectSyntax constantObjectSyntax,
                                                                         Type[] interfaces,
                                                                         params IInterceptorProcess[]
                                                                             interceptorProcesses)
        {
            Validate.ArgumentNotNull(constantObjectSyntax, "constantObjectSyntax");
            Validate.ArgumentNotNull(interceptorProcesses, "interceptorProcesses");
            return InterceptAsTargetInternal<IInterceptAsTargetConstantSyntax>(constantObjectSyntax, interfaces,
                                                                               interceptorProcesses, false);
        }

        /// <summary>
        /// Indicates that instances associated with this binding will be proxied and the target will be to the original binding.
        /// </summary>
        /// <param name="constantObjectSyntax">The binding syntax target.</param>
        /// <param name="interceptorProcesses">The specified <see cref="IInterceptorProcess"/> for intercept.</param>
        /// <returns>The fluent syntax.</returns>
        public static IInterceptAsTargetConstantSyntax InterceptAsTarget(IConstantObjectSyntax constantObjectSyntax,
                                                                         params IInterceptorProcess[]
                                                                             interceptorProcesses)
        {
            Validate.ArgumentNotNull(constantObjectSyntax, "constantObjectSyntax");
            Validate.ArgumentNotNull(interceptorProcesses, "interceptorProcesses");
            return InterceptAsTargetInternal<IInterceptAsTargetConstantSyntax>(constantObjectSyntax,
                                                                               InjectorUtils.EmptyTypes,
                                                                               interceptorProcesses, false);
        }

        /// <summary>
        /// Indicates that instances associated with this binding will be proxied and the target will be to the original binding.
        /// </summary>
        /// <param name="methodObjectSyntax">The binding syntax target.</param>
        /// <param name="interfaces">Interfaces for proxy.</param>
        /// <param name="interceptorProcesses">The specified <see cref="IInterceptorProcess"/> for intercept.</param>
        /// <returns>The fluent syntax.</returns>
        public static IInterceptAsTargetMethodSyntax InterceptAsTarget(IMethodObjectSyntax methodObjectSyntax,
                                                                       Type[] interfaces,
                                                                       params IInterceptorProcess[] interceptorProcesses)
        {
            Validate.ArgumentNotNull(methodObjectSyntax, "methodObjectSyntax");
            Validate.ArgumentNotNull(interceptorProcesses, "interceptorProcesses");
            return InterceptAsTargetInternal<IInterceptAsTargetMethodSyntax>(methodObjectSyntax, interfaces,
                                                                             interceptorProcesses, false);
        }

        /// <summary>
        /// Indicates that instances associated with this binding will be proxied and the target will be to the original binding.
        /// </summary>
        /// <param name="methodObjectSyntax">The binding syntax target.</param>
        /// <param name="interceptorProcesses">The specified <see cref="IInterceptorProcess"/> for intercept.</param>
        /// <returns>The fluent syntax.</returns>
        public static IInterceptAsTargetMethodSyntax InterceptAsTarget(IMethodObjectSyntax methodObjectSyntax,
                                                                       params IInterceptorProcess[] interceptorProcesses)
        {
            Validate.ArgumentNotNull(methodObjectSyntax, "methodObjectSyntax");
            Validate.ArgumentNotNull(interceptorProcesses, "interceptorProcesses");
            return InterceptAsTargetInternal<IInterceptAsTargetMethodSyntax>(methodObjectSyntax,
                                                                             InjectorUtils.EmptyTypes,
                                                                             interceptorProcesses, false);
        }

        /// <summary>
        /// Indicates that instances associated with this binding will be proxied and the target will be to the original binding.
        /// </summary>
        /// <param name="typeObjectSyntax">The binding syntax target.</param>
        /// <param name="interfaces">Interfaces for proxy.</param>
        /// <param name="interceptorProcesses">The specified <see cref="IInterceptorProcess"/> for intercept.</param>
        /// <returns>The fluent syntax.</returns>
        public static IInterceptAsTargetTypeSyntax InterceptAsTarget(ITypeObjectSyntax typeObjectSyntax,
                                                                     Type[] interfaces,
                                                                     params IInterceptorProcess[] interceptorProcesses)
        {
            Validate.ArgumentNotNull(typeObjectSyntax, "typeObjectSyntax");
            Validate.ArgumentNotNull(interceptorProcesses, "interceptorProcesses");
            return InterceptAsTargetInternal<IInterceptAsTargetTypeSyntax>(typeObjectSyntax, interfaces,
                                                                           interceptorProcesses, false);
        }

        /// <summary>
        /// Indicates that instances associated with this binding will be proxied and the target will be to the original binding.
        /// </summary>
        /// <param name="typeObjectSyntax">The binding syntax target.</param>
        /// <param name="interceptorProcesses">The specified <see cref="IInterceptorProcess"/> for intercept.</param>
        /// <returns>The fluent syntax.</returns>
        public static IInterceptAsTargetTypeSyntax InterceptAsTarget(ITypeObjectSyntax typeObjectSyntax,
                                                                     params IInterceptorProcess[] interceptorProcesses)
        {
            Validate.ArgumentNotNull(typeObjectSyntax, "typeObjectSyntax");
            Validate.ArgumentNotNull(interceptorProcesses, "interceptorProcesses");
            return InterceptAsTargetInternal<IInterceptAsTargetTypeSyntax>(typeObjectSyntax, InjectorUtils.EmptyTypes,
                                                                           interceptorProcesses, false);
        }

        //------------------------

        /// <summary>
        /// Indicates that instances associated with this binding will be proxied and the target will be to the original binding.
        /// </summary>
        /// <param name="constantObjectSyntax">The binding syntax target.</param>
        /// <param name="interfaces">Interfaces for proxy.</param>
        /// <param name="interceptorProcesses">The specified <see cref="IInterceptorProcess"/> for intercept.</param>
        /// <returns>The fluent syntax.</returns>
        public static IInterceptAsTargetConstantSyntax<T> InterceptAsTarget<T>(
            IConstantObjectSyntax<T> constantObjectSyntax, Type[] interfaces,
            params IInterceptorProcess[] interceptorProcesses)
        {
            Validate.ArgumentNotNull(constantObjectSyntax, "constantObjectSyntax");
            Validate.ArgumentNotNull(interceptorProcesses, "interceptorProcesses");
            return InterceptAsTargetInternal<T, IInterceptAsTargetConstantSyntax<T>>(constantObjectSyntax, interfaces,
                                                                                     interceptorProcesses, false);
        }

        /// <summary>
        /// Indicates that instances associated with this binding will be proxied and the target will be to the original binding.
        /// </summary>
        /// <param name="constantObjectSyntax">The binding syntax target.</param>
        /// <param name="interceptorProcesses">The specified <see cref="IInterceptorProcess"/> for intercept.</param>
        /// <returns>The fluent syntax.</returns>
        public static IInterceptAsTargetConstantSyntax<T> InterceptAsTarget<T>(IConstantObjectSyntax<T> constantObjectSyntax,
            params IInterceptorProcess[] interceptorProcesses)
        {
            Validate.ArgumentNotNull(constantObjectSyntax, "constantObjectSyntax");
            Validate.ArgumentNotNull(interceptorProcesses, "interceptorProcesses");
            return InterceptAsTargetInternal<T, IInterceptAsTargetConstantSyntax<T>>(constantObjectSyntax,
                                                                                     InjectorUtils.EmptyTypes,
                                                                                     interceptorProcesses, false);
        }

        /// <summary>
        /// Indicates that instances associated with this binding will be proxied and the target will be to the original binding.
        /// </summary>
        /// <param name="methodObjectSyntax">The binding syntax target.</param>
        /// <param name="interfaces">Interfaces for proxy.</param>
        /// <param name="interceptorProcesses">The specified <see cref="IInterceptorProcess"/> for intercept.</param>
        /// <returns>The fluent syntax.</returns>
        public static IInterceptAsTargetMethodSyntax<T> InterceptAsTarget<T>(IMethodObjectSyntax<T> methodObjectSyntax,
                                                                             Type[] interfaces,
                                                                             params IInterceptorProcess[]
                                                                                 interceptorProcesses)
        {
            Validate.ArgumentNotNull(methodObjectSyntax, "methodObjectSyntax");
            Validate.ArgumentNotNull(interceptorProcesses, "interceptorProcesses");
            return InterceptAsTargetInternal<T, IInterceptAsTargetMethodSyntax<T>>(methodObjectSyntax, interfaces,
                                                                                   interceptorProcesses, false);
        }

        /// <summary>
        /// Indicates that instances associated with this binding will be proxied and the target will be to the original binding.
        /// </summary>
        /// <param name="methodObjectSyntax">The binding syntax target.</param>
        /// <param name="interceptorProcesses">The specified <see cref="IInterceptorProcess"/> for intercept.</param>
        /// <returns>The fluent syntax.</returns>
        public static IInterceptAsTargetMethodSyntax<T> InterceptAsTarget<T>(IMethodObjectSyntax<T> methodObjectSyntax,
                                                                             params IInterceptorProcess[]
                                                                                 interceptorProcesses)
        {
            Validate.ArgumentNotNull(methodObjectSyntax, "methodObjectSyntax");
            Validate.ArgumentNotNull(interceptorProcesses, "interceptorProcesses");
            return InterceptAsTargetInternal<T, IInterceptAsTargetMethodSyntax<T>>(methodObjectSyntax,
                                                                                   InjectorUtils.EmptyTypes,
                                                                                   interceptorProcesses, false);
        }

        /// <summary>
        /// Indicates that instances associated with this binding will be proxied and the target will be to the original binding.
        /// </summary>
        /// <param name="typeObjectSyntax">The binding syntax target.</param>
        /// <param name="interfaces">Interfaces for proxy.</param>
        /// <param name="interceptorProcesses">The specified <see cref="IInterceptorProcess"/> for intercept.</param>
        /// <returns>The fluent syntax.</returns>
        public static IInterceptAsTargetTypeSyntax<T> InterceptAsTarget<T>(ITypeObjectSyntax<T> typeObjectSyntax,
                                                                           Type[] interfaces,
                                                                           params IInterceptorProcess[]
                                                                               interceptorProcesses)
        {
            Validate.ArgumentNotNull(typeObjectSyntax, "typeObjectSyntax");
            Validate.ArgumentNotNull(interceptorProcesses, "interceptorProcesses");
            return InterceptAsTargetInternal<T, IInterceptAsTargetTypeSyntax<T>>(typeObjectSyntax, interfaces,
                                                                                 interceptorProcesses, false);
        }

        /// <summary>
        /// Indicates that instances associated with this binding will be proxied and the target will be to the original binding.
        /// </summary>
        /// <param name="typeObjectSyntax">The binding syntax target.</param>
        /// <param name="interceptorProcesses">The specified <see cref="IInterceptorProcess"/> for intercept.</param>
        /// <returns>The fluent syntax.</returns>
        public static IInterceptAsTargetTypeSyntax<T> InterceptAsTarget<T>(ITypeObjectSyntax<T> typeObjectSyntax,
                                                                           params IInterceptorProcess[]
                                                                               interceptorProcesses)
        {
            Validate.ArgumentNotNull(typeObjectSyntax, "typeObjectSyntax");
            Validate.ArgumentNotNull(interceptorProcesses, "interceptorProcesses");
            return InterceptAsTargetInternal<T, IInterceptAsTargetTypeSyntax<T>>(typeObjectSyntax,
                                                                                 InjectorUtils.EmptyTypes,
                                                                                 interceptorProcesses, false);
        }


        //------------

        /// <summary>
        /// Intercepts a get property method.
        /// </summary>
        /// <param name="interceptSyntax">The binding syntax target.</param>
        /// <param name="propertyInfo">The specified <see cref="PropertyInfo"/> for intercept.</param>
        /// <param name="interceptAction">The specified delegate for intercept.</param>
        /// <returns>The fluent syntax.</returns>
        public static TSyntax InterceptGetProperty<TSyntax>(TSyntax interceptSyntax, PropertyInfo propertyInfo, Func<IPropertyGetInterceptor, object> interceptAction)
            where TSyntax : IInterceptSyntax
        {
            Validate.ArgumentNotNull(interceptSyntax, "interceptSyntax");
            Validate.ArgumentNotNull(propertyInfo, "propertyInfo");
            Validate.ArgumentNotNull(interceptAction, "interceptAction");
            AddActionInterceptor(GetOrAddActionInterceptorProcess(interceptSyntax), InterceptorType.PropertyGet,
                                 propertyInfo, interceptor =>
                                     {
                                         var getInterceptor = (IPropertyGetInterceptor)interceptor;
                                         getInterceptor.GetValue = interceptAction(getInterceptor);
                                     });
            return interceptSyntax;
        }

        /// <summary>
        /// Intercepts a set property method.
        /// </summary>
        /// <param name="interceptSyntax">The binding syntax target.</param>
        /// <param name="propertyInfo">The specified <see cref="PropertyInfo"/> for intercept.</param>
        /// <param name="interceptAction">The specified delegate for intercept.</param>
        /// <returns>The fluent syntax.</returns>
        public static TSyntax InterceptSetProperty<TSyntax>(TSyntax interceptSyntax, PropertyInfo propertyInfo, Func<IPropertySetInterceptor, object> interceptAction)
            where TSyntax : IInterceptSyntax
        {
            Validate.ArgumentNotNull(interceptSyntax, "interceptSyntax");
            Validate.ArgumentNotNull(propertyInfo, "propertyInfo");
            Validate.ArgumentNotNull(interceptAction, "interceptAction");
            AddActionInterceptor(GetOrAddActionInterceptorProcess(interceptSyntax), InterceptorType.PropertySet,
                                 propertyInfo, interceptor =>
                                 {
                                     var setInterceptor = (IPropertySetInterceptor)interceptor;
                                     setInterceptor.SetValue = interceptAction(setInterceptor);
                                     setInterceptor.SetMethod();
                                 });
            return interceptSyntax;
        }

        /// <summary>
        /// Intercepts adding an event.
        /// </summary>
        /// <param name="interceptSyntax">The binding syntax target.</param>
        /// <param name="eventInfo">The specified <see cref="EventInfo"/> for intercept.</param>
        /// <param name="interceptAction">The specified delegate for intercept.</param>
        /// <returns>The fluent syntax.</returns>
        public static TSyntax InterceptAddEvent<TSyntax>(TSyntax interceptSyntax, EventInfo eventInfo, Func<IEventAddInterceptor, Delegate> interceptAction)
            where TSyntax : IInterceptSyntax
        {
            Validate.ArgumentNotNull(interceptSyntax, "interceptSyntax");
            Validate.ArgumentNotNull(eventInfo, "eventInfo");
            Validate.ArgumentNotNull(interceptAction, "interceptAction");
            AddActionInterceptor(GetOrAddActionInterceptorProcess(interceptSyntax), InterceptorType.EventAdd,
                                 eventInfo, interceptor =>
                                 {
                                     var addInterceptor = (IEventAddInterceptor)interceptor;
                                     addInterceptor.AddValue = interceptAction(addInterceptor);
                                     addInterceptor.AddMethod();
                                 });
            return interceptSyntax;
        }

        /// <summary>
        /// Intercepts removing an event.
        /// </summary>
        /// <param name="interceptSyntax">The binding syntax target.</param>
        /// <param name="eventInfo">The specified <see cref="EventInfo"/> for intercept.</param>
        /// <param name="interceptAction">The specified delegate for intercept.</param>
        /// <returns>The fluent syntax.</returns>
        public static TSyntax InterceptRemoveEvent<TSyntax>(TSyntax interceptSyntax, EventInfo eventInfo, Func<IEventRemoveInterceptor, Delegate> interceptAction)
            where TSyntax : IInterceptSyntax
        {
            Validate.ArgumentNotNull(interceptSyntax, "interceptSyntax");
            Validate.ArgumentNotNull(eventInfo, "eventInfo");
            Validate.ArgumentNotNull(interceptAction, "interceptAction");
            AddActionInterceptor(GetOrAddActionInterceptorProcess(interceptSyntax), InterceptorType.EventRemove,
                                 eventInfo, interceptor =>
                                 {
                                     var removeInterceptor = (IEventRemoveInterceptor)interceptor;
                                     removeInterceptor.RemoveValue = interceptAction(removeInterceptor);
                                     removeInterceptor.RemoveMethod();
                                 });
            return interceptSyntax;
        }

        /// <summary>
        /// Intercept a method invoke.
        /// </summary>
        /// <param name="interceptSyntax">The binding syntax target.</param>
        /// <param name="method">The specified <see cref="MethodInfo"/> for intercept.</param>
        /// <param name="interceptAction">The specified delegate for intercept.</param>
        /// <returns>The fluent syntax.</returns>
        public static TSyntax InterceptInvokeMethod<TSyntax>(TSyntax interceptSyntax, MethodInfo method, Func<IMethodInterceptor, object> interceptAction)
            where TSyntax : IInterceptSyntax
        {
            Validate.ArgumentNotNull(interceptSyntax, "interceptSyntax");
            Validate.ArgumentNotNull(method, "method");
            Validate.ArgumentNotNull(interceptAction, "interceptAction");
            AddActionInterceptor(GetOrAddActionInterceptorProcess(interceptSyntax), InterceptorType.Method,
                                 method, interceptor =>
                                     {
                                         var methodInterceptor = (IMethodInterceptor) interceptor;
                                         methodInterceptor.ReturnValue = interceptAction(methodInterceptor);
                                     });
            return interceptSyntax;
        }

        #endregion
    }
}